package org.yaac.client.ui;

import static com.google.common.base.Strings.isNullOrEmpty;

import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import javax.inject.Inject;

import org.yaac.client.activity.BlobActivity;
import org.yaac.client.util.AutoBeanUtil;
import org.yaac.client.widget.BlockingAsyncCallback;
import org.yaac.client.widget.SortHandler;
import org.yaac.client.widget.SortableColumn;
import org.yaac.shared.blob.BlobInfoDTO;
import org.yaac.shared.file.BlobUploadResult;
import org.yaac.shared.file.BlobUploadResult.Status;
import org.yaac.shared.file.FileDownloadPath;

import com.google.common.base.Predicate;

import com.google.common.collect.Collections2;
import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.EditTextCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.DataGrid;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.DefaultSelectionEventManager;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionModel;

/**
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
// TODO : add delete all / send all to me feature
public class BlobViewImpl extends Composite implements BlobView {

	private static BlobViewUiBinder uiBinder = GWT.create(BlobViewUiBinder.class);

	@UiTemplate("BlobView.ui.xml")
	interface BlobViewUiBinder extends UiBinder<Widget, BlobViewImpl> {
	}

	// ============blob filter and data============
	@UiField
	TextBox filterTxt;
	
	// internal status for filters text
	private boolean filterTxtWaitingForFirstClick;
	
	@UiField(provided=true)
	DataGrid<BlobInfoDTO> blobInfoTable;
	
	private SelectionModel<BlobInfoDTO> selectionModel;
	
	private ListDataProvider<BlobInfoDTO> dataProvider;
	
	private SortHandler<BlobInfoDTO> sortHandler;
	
	// =============blob upload============
	@UiField
	FormPanel form;
	
	@UiField
	CheckBox selectAllCb;
	
	@UiField
	Button deleteBtn;
	
	@UiField
	TextBox nameTxt;
	
	// internal status for filters text
	private boolean nameTxtWaitingForFirstClick;
	
	@UiField
	FileUpload upload;
	
	private BlobActivity listenr;
	
	@Inject
	BlobViewImpl() {
		// init blobInfoTable
		initBlobInfoTable();
		
		initWidget(uiBinder.createAndBindUi(this));
		
		this.filterTxtWaitingForFirstClick = true;
		this.nameTxtWaitingForFirstClick = true;
	}
	
	private void initBlobInfoTable() {
		// step 1 
		sortHandler = new SortHandler<BlobInfoDTO>();
		
		blobInfoTable = new DataGrid<BlobInfoDTO>();
		blobInfoTable.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
		blobInfoTable.addColumnSortHandler(sortHandler);
		blobInfoTable.setVisibleRange(0, Integer.MAX_VALUE);
		selectionModel = new MultiSelectionModel<BlobInfoDTO>(BlobInfoDTO.KEY_PROVIDER);
		blobInfoTable.setSelectionModel(selectionModel, DefaultSelectionEventManager.<BlobInfoDTO>createCheckboxManager());
		
		// step 2 : add columns
		
		// checkbox for multi selection, unsortable
		{
		    Column<BlobInfoDTO, Boolean> checkColumn = new Column<BlobInfoDTO, Boolean>(
		            new CheckboxCell(true, false)) {
		          @Override
		          public Boolean getValue(BlobInfoDTO object) {
		        	  return selectionModel.isSelected(object);
		          }
		    };
		    
		    //empty nothing
		    blobInfoTable.addColumn(checkColumn, SafeHtmlUtils.fromSafeConstant("<br/>"));	
		}
		
		// filename
		{
			SortableColumn<BlobInfoDTO, SafeHtml> column = 
				new SortableColumn<BlobInfoDTO, SafeHtml>(new SafeHtmlCell()) {
				@Override
				public SafeHtml getValue(BlobInfoDTO o) {				
					FileDownloadPath path = AutoBeanUtil.blobDownloadPath(o.getBlobKeyStr());
					String pathStr = AutoBeanUtil.encode(FileDownloadPath.class, path);
					SafeHtmlBuilder sb = new SafeHtmlBuilder();
					
					sb.appendHtmlConstant("<a href='/download?path=" + SafeHtmlUtils.htmlEscape(pathStr) + "' target='blank'>");
					sb.appendHtmlConstant(o.getFilename());
					sb.appendHtmlConstant("</a>");
					
					return sb.toSafeHtml();
				}
			};
			
			sortHandler.setComparator(column, new Comparator<BlobInfoDTO>() {
				@Override
				public int compare(BlobInfoDTO o1, BlobInfoDTO o2) {
					return o1.getFilename().compareTo(o2.getFilename());
				}
			});
			
			blobInfoTable.addColumn(column, "File Name");
		}
		
		// contentType
		{
			SortableColumn<BlobInfoDTO, String> column = 
				new SortableColumn<BlobInfoDTO, String>(new TextCell()) {
				@Override
				public String getValue(BlobInfoDTO o) {			
					return o.getContentType();
				}
			};
			
			sortHandler.setComparator(column, new Comparator<BlobInfoDTO>() {
				@Override
				public int compare(BlobInfoDTO o1, BlobInfoDTO o2) {
					return o1.getContentType().compareTo(o2.getContentType());
				}
			});
			
			blobInfoTable.addColumn(column, "Content Type");
		}
		
		// size
		{
			SortableColumn<BlobInfoDTO, String> column = 
				new SortableColumn<BlobInfoDTO, String>(new TextCell()) {
				@Override
				public String getValue(BlobInfoDTO o) {			
					return o.getSize().toString();
				}
			};
			
			sortHandler.setComparator(column, new Comparator<BlobInfoDTO>() {
				@Override
				public int compare(BlobInfoDTO o1, BlobInfoDTO o2) {
					return o1.getSize().compareTo(o2.getSize());
				}
			});
			
			blobInfoTable.addColumn(column, "Size");
		}
		
		// creation date
		{
			SortableColumn<BlobInfoDTO, String> column = 
				new SortableColumn<BlobInfoDTO, String>(new TextCell()) {
				@Override
				public String getValue(BlobInfoDTO o) {			
					return o.getCreation().toString();
				}
			};
			
			sortHandler.setComparator(column, new Comparator<BlobInfoDTO>() {
				@Override
				public int compare(BlobInfoDTO o1, BlobInfoDTO o2) {
					return o1.getCreation().compareTo(o2.getCreation());
				}
			});
			
			blobInfoTable.addColumn(column, "Creation Date");
		}
		
		// blob key hash
		{
			SortableColumn<BlobInfoDTO, String> column = 
				new SortableColumn<BlobInfoDTO, String>(new EditTextCell()) {
				@Override
				public String getValue(BlobInfoDTO o) {			
					return o.getBlobKeyStr();
				}
			};
			
			column.setFieldUpdater(new FieldUpdater<BlobInfoDTO, String>() {
				@Override
				public void update(int index, BlobInfoDTO object, String value) {
					// do nothing, simply provide a way for users to copy value
				}
			});
			
			blobInfoTable.addColumn(column,  SafeHtmlUtils.fromSafeConstant("Key String<br/>(click to select)"));
			blobInfoTable.setColumnWidth(column, "100pt");
		}
		
		// md5 hash
		{
			SortableColumn<BlobInfoDTO, String> column = 
				new SortableColumn<BlobInfoDTO, String>(new EditTextCell()) {
				@Override
				public String getValue(BlobInfoDTO o) {			
					return o.getMd5Hash();
				}
			};
			
			column.setFieldUpdater(new FieldUpdater<BlobInfoDTO, String>() {
				@Override
				public void update(int index, BlobInfoDTO object, String value) {
					// do nothing, simply provide a way for users to copy value
				}
			});
			
			blobInfoTable.addColumn(column,  SafeHtmlUtils.fromSafeConstant("MD5 Hash<br/>(click to select)"));
			blobInfoTable.setColumnWidth(column, "100pt");
		}
	}

	@Override
	public void setPresenter(BlobActivity listener) {
		this.listenr = listener;
	}

	@UiHandler("filterTxt")
	void onFilterTxtClick(ClickEvent event) {
		if (filterTxtWaitingForFirstClick) {
			filterTxt.setText("");
			filterTxtWaitingForFirstClick = false;
		}
	}
	
	private String filterTxtContent() {
		if (filterTxtWaitingForFirstClick) {
			return "";
		} else {
			return filterTxt.getText();
		}
	}
	
	@UiHandler("filterTxt")
	void onFilterTxtKeyPress(KeyPressEvent event) {
		char key = event.getCharCode();
		if (KeyCodes.KEY_ENTER == key) {
			this.listenr.refreshBlobInfo();
		}
	}

	@Override
	public void updateBlobInfoList(List<BlobInfoDTO> result) {
		if (dataProvider == null) {
			dataProvider = new ListDataProvider<BlobInfoDTO>(new LinkedList<BlobInfoDTO>());
			dataProvider.addDataDisplay(blobInfoTable);
			sortHandler.setDataProvider(dataProvider);
		}
		
		dataProvider.getList().clear();
		dataProvider.getList().addAll(result);
	}
	
	@UiHandler("selectAllCb")
	void onSelectAllCbChange(ValueChangeEvent<Boolean> event) {
		for (BlobInfoDTO blob : blobInfoTable.getVisibleItems()) {
			selectionModel.setSelected(blob, event.getValue());
		}
	}
	
	@UiHandler("deleteBtn")
	void onDeleteBtnClick(ClickEvent event) {		
		Collection<BlobInfoDTO> selectedBlobs = 
			Collections2.filter(dataProvider.getList(), new Predicate<BlobInfoDTO>() {
			@Override
			public boolean apply(BlobInfoDTO input) {
				return selectionModel.isSelected(input);
			}
		});
		
		if (selectedBlobs.isEmpty()) {
			Window.alert("No record selected");
			return;
		} 
		
		if (Window.confirm("Are you sure you want to delete " + selectedBlobs.size() + " file(s)")) {
			listenr.delete(selectedBlobs);
		}
	}
	
	@UiHandler("nameTxt")
	void onNameTxtClick(ClickEvent event) {
		if (nameTxtWaitingForFirstClick) {
			nameTxt.setText("");
			nameTxtWaitingForFirstClick = false;
		}
	}
	
	private String nameTxtContent() {
		if (nameTxtWaitingForFirstClick) {
			return "";
		} else {
			return nameTxt.getText();
		}
	}
	
	/**
	 * @param event
	 */
	@UiHandler("form")
	void onFormSubmit(SubmitEvent event) {
		if (validate()) {
			BlockingAsyncCallback.block("Uploading");	
		} else {
			event.cancel();
		}
	}
	
	private boolean validate() {
		if (isNullOrEmpty(nameTxtContent().trim())) {
			Window.alert("You must specify the filename"); 	// TODO : i18n
			return false;
		}
		
		if (isNullOrEmpty(upload.getFilename().trim())) {
			Window.alert("You must select a file to upload");	// TODO : i18n 
			return false;
		}
		
		return true;
	}
	
	@UiHandler("form")
	void onFormSubmitComplete(SubmitCompleteEvent event) {		
		BlockingAsyncCallback.unblock();
		
		// if event.getResults returns null, then the form is not even submitted
		if (!isNullOrEmpty(event.getResults())) {	// print out result
			BlobUploadResult result = AutoBeanUtil.decode(BlobUploadResult.class, event.getResults());
			
			if (result.getStatus() == Status.SUCCESS) {
				// TODO : i18n
				Window.alert("File " + result.getFileName() + " (size = " + result.getSize() + ") is successfully uploaded");
				form.reset();
				listenr.refreshBlobInfo();
			} else {	// upload failed
				Window.alert("Can't upload file");	// TODO : i18n
			}
		}
	}

	@Override
	public String filterStr() {
		return filterTxtContent();
	}
}
